#if UNITY_EDITOR
using UnityEditor;
using UnityEngine.PlayerLoop;
using UnityEngine.UI;
#endif

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Object = UnityEngine.Object;

namespace XFramework
{
    [Serializable]
    public class Bind
    {
        public string Name;
        public Object Object;
    }

    [RequireComponent(typeof(CodeController))]
    public class Binds : MonoBehaviour
    {
        public List<Bind> binds = new List<Bind>();
    }

#if UNITY_EDITOR
    [CustomEditor(typeof(Binds))]
    public class BindsInspector : Editor
    {
        private Binds binds;

        private void OnEnable()
        {
            binds = (Binds)target;
        }

        public override void OnInspectorGUI()
        {
            base.OnInspectorGUI();

            Undo.RecordObject(binds, "Changed Settings");
            var dataProperty = serializedObject.FindProperty("binds");

            EditorGUILayout.Space();

            var delList = new List<int>();
            SerializedProperty property;

            for (int i = binds.binds.Count - 1; i >= 0; i--)
            {
                {
                    GUILayout.BeginHorizontal();
                    var sp = dataProperty.GetArrayElementAtIndex(i);
                    property = sp.FindPropertyRelative("Name");
                    property.stringValue = EditorGUILayout.TextField(property.stringValue, GUILayout.Width(130));
                    property = sp.FindPropertyRelative("Object");
                    property.objectReferenceValue =
                        EditorGUILayout.ObjectField(property.objectReferenceValue, typeof(Object), true);

                    if (property.objectReferenceValue is Component component)
                    {
                        var objs = new List<Object>();
                        objs.AddRange(component.gameObject.GetComponents<Component>());
                        objs.Add(component.gameObject);

                        var index = objs.FindIndex(c => c.GetType() == property.objectReferenceValue.GetType());
                        var newIdx = EditorGUILayout.Popup(index, objs.Select(c => c.GetType().FullName).ToArray());
                        if (newIdx != index)
                        {
                            property.objectReferenceValue = objs[newIdx];
                        }
                    }
                    else if (property.objectReferenceValue is GameObject gameObject)
                    {
                        var objs = new List<Object>();
                        objs.AddRange(gameObject.GetComponents<Component>());
                        objs.Add(gameObject);

                        var index = objs.FindIndex(c => c.GetType() == property.objectReferenceValue.GetType());
                        var newIndex =
                            EditorGUILayout.Popup(index, objs.Select(c => c.GetType().FullName).ToArray());
                        if (index != newIndex)
                        {
                            property.objectReferenceValue = objs[newIndex];
                        }
                    }

                    if (GUILayout.Button("X"))
                    {
                        delList.Add(i);
                    }

                    GUILayout.EndHorizontal();
                }
            }

            EditorGUILayout.Space();
            GUILayout.Label("将需要生成变量的 Object 拖拽至此");
            var sfxPathRect = EditorGUILayout.GetControlRect();
            sfxPathRect.height = 50;
            GUI.Box(sfxPathRect, string.Empty);

            EditorGUILayout.LabelField(string.Empty, GUILayout.Height(30));
            if ((Event.current.type == EventType.DragUpdated || Event.current.type == EventType.DragPerform)
                && sfxPathRect.Contains(Event.current.mousePosition))
            {
                DragAndDrop.visualMode = DragAndDropVisualMode.Copy;

                if (Event.current.type == EventType.DragPerform)
                {
                    DragAndDrop.AcceptDrag();

                    foreach (var or in DragAndDrop.objectReferences)
                    {
                        if (or.name == target.name)
                        {
                            addReference(dataProperty, $"Self{or.GetType().Name}", or);
                        }
                        else
                        {
                            addReference(dataProperty, or.name.Remove(" ", "-", "@"), or);
                        }
                    }
                }

                Event.current.Use();
            }

            foreach (var i in delList)
            {
                dataProperty.DeleteArrayElementAtIndex(i);
            }

            serializedObject.ApplyModifiedProperties();
            serializedObject.UpdateIfRequiredOrScript();
        }

        private void addReference(SerializedProperty dataProperty, string key, Object obj)
        {
            var index = dataProperty.arraySize;
            dataProperty.InsertArrayElementAtIndex(index);
            var element = dataProperty.GetArrayElementAtIndex(index);
            element.FindPropertyRelative("Name").stringValue = key;
            element.FindPropertyRelative("Object").objectReferenceValue = obj;
        }
    }
#endif
}